home *** CD-ROM | disk | FTP | other *** search
Text File | 1989-03-12 | 12.6 KB | 424 lines | [TEXT/KAHL] |
- /*
- * Written by Leonard Rosenthol, Copyright©1988 by LazerWare, inc.
- *
- * This is sample source for a CDEF. In particular it embodies all the Apple Human
- * Interface Guidelines dealing with popup menus, as well as those of BMUG DevSig.
- *
- * To use it define or create a control whose contrlRfCon field contains the menu id
- * of a menu to be used as a popup menu. If the menu is to be created by the control
- * it must have a MENU resource definition with a resource id the same as the menu id
- * field. If the menu is created by the program before the control is created it must
- * be inserted into the menu bar with InsertMenu(mh, hierMenu).
- *
- * If the control has a title it will be displayed to the left of the popup menu. The
- * menu's title is ignored.
- *
- * If the variant code (low 4 bits of the control def id) is zero the drawn menu will
- * be left justified in the given rectangle, otherwise it will be centered.
- *
- * This code was compiled under LSC 3.01p4
- */
-
-
- /* predefine some miscellaneous functions */
- void CalcPopUpRect();
- MenuHandle GetMenuHandle();
-
- #define HSeparation 0 /* distance between label and popup box */
- #define HSpace 3 /* horizontal whitespace around popup box */
- #define VSpace 1 /* vertical whitespace around popup box */
- #define NULL 0L /* Just what you think it is */
-
- /* draw the control upon the screen. Note that there are several possible
- cases here:
-
- contrlVis is 0: The control isn't visible. No real reason for us to
- have gotten here, but in any case, we don't draw anything
-
- param: This is nominally the part that needs redrawing. Since
- we don't have many parts we just always draw them all. There
- is a special case value of 129 which means that the value
- has been changed and appropriate measures need to be taken.
- We use this to uncheck the old value and check the new one.
-
- contrlHilite is 255: The control is inactive. It get's drawn, just like
- normal, but then grey'ed out.
- */
- void DrawPopUp(varcode, theControl, param)
- int varcode;
- ControlHandle theControl;
- long param;
- {
- char string[256];
- MenuHandle mh;
- Rect rect;
- int numItems, counter, checkWidth = CharWidth(0x12);
-
- /* save our selves a lot of work if the control isn't visible */
- if((**theControl).contrlVis == 0)
- return;
-
- /* get the menu handle since we're going to need it eventually */
- mh = GetMenuHandle(theControl);
-
- /* get the actual bounds of the item, this varies as the length
- of the string displayed does since it looks sick to have this
- big long rectangle with "1" in it */
- CalcPopUpRect(varcode, theControl, &rect);
-
- /* if the control has a title draw it in */
- if(*(**theControl).contrlTitle)
- {
- MoveTo((**theControl).contrlRect.left, rect.bottom - GetFontOffset() - VSpace);
- DrawString((**theControl).contrlTitle);
- }
-
- /* if for some reason the menu hasn't been defined, never fear
- we just draw the title and exit */
- if(mh)
- {
- /* get the currently selected item's string */
- GetItem(mh, (**theControl).contrlValue, string);
-
- /* erase, frame and drop shadow the future menu item */
- EraseRect(&rect);
- FrameRect(&rect);
- MoveTo(rect.left + 1, rect.bottom);
- LineTo(rect.right, rect.bottom);
- LineTo(rect.right, rect.top + 1);
-
- /* draw the actual menu item itself */
- /* we now leave room for the checkMark! */
- MoveTo(rect.left + /*HSpace+*/ checkWidth, rect.bottom - GetFontOffset() - VSpace);
- DrawString(string);
-
- /* Reset the checkMark to the currently selected item! */
-
- numItems = CountMItems(mh);
- for (counter=1; counter<=numItems;counter++)
- CheckItem(mh, counter, FALSE); /* Turn them all off!! */
-
- CheckItem(mh, (**theControl).contrlValue, TRUE); /* Turn on the one! */
- }
-
- /* the control is inactive so grey it out */
- if((**theControl).contrlHilite == 255)
- {
- PenState ps;
- long gray[2];
-
- /* save away the old pen parameters */
- GetPenState(&ps);
-
- /* good pattern for clearing bits */
- PenMode(patBic);
-
- /* 50% grey pattern so's we don't have to rely on finding one
- somewhere */
- gray[0] = 0xaa55aa55;
- gray[1] = 0xaa55aa55;
- PenPat(gray);
-
- /* grey out the whole control, including the title */
- rect.left = (**theControl).contrlRect.left;
- InsetRect(&rect, -1, -1);
- PaintRect(&rect);
-
- /* restore the saved parameters */
- SetPenState(&ps);
- }
- }
-
- /* check to see if a click is in our control */
- HitPopUp(varcode, theControl, param)
- int varcode;
- ControlHandle theControl;
- Point param;
- {
- Rect rect;
-
- /* if the control isn't visible click obviously isn't ours */
- if((**theControl).contrlVis)
- {
- /* find out portion of the dialog the popup portion actually
- occupies and check to see if the mouse is in it */
- CalcPopUpRect(varcode, theControl, &rect);
- if(PtInRect(param, &rect))
- {
- /* if so return like a button so TrackControl will
- be called as appropriate */
- return inButton;
- }
- else
- return 0;
- }
-
- return 0;
- }
-
- /* compute a region which looks like our control. Actually pretty
- easy, it's a rectangle the size of the control */
- void CalcPopUp(varcode, theControl, param)
- int varcode;
- ControlHandle theControl;
- RgnHandle param;
- {
- Rect rect;
-
- CalcPopUpRect(varcode, theControl, &rect);
-
- OpenRgn();
- FrameRect(&(**theControl).contrlRect);
- CloseRgn(param);
- }
-
- /* called when the control is first created, it has two major
- objectives, stow the MenuHandle away in the data and create
- the menu if necessary. */
- void NewPopUp(varcode, theControl, param)
- int varcode;
- ControlHandle theControl;
- long param;
- {
- MenuHandle mh;
- Rect rect, myRect;
- int myType;
- Handle myHandle;
-
- /* see if the menu has already been created by somebody,
- the menu id (must be same as resource id) is in the
- refcon field */
- if((mh = GetMHandle((long) (**theControl).contrlRfCon)) == 0)
- {
- /* nope, the menu doesn't already exist so create it. If
- that doesn't work we just don't display a menu */
- if((mh = GetMenu((long) (**theControl).contrlRfCon)) == 0)
- mh = NULL;
- else
- /* put the newly created menu in the menu bar as a
- hierarchical menu so future GetMHandle's will
- find it */
- InsertMenu(mh, hierMenu);
- }
-
- /* stow the MenuHandle away in the data field like we
- promised we would */
- (**theControl).contrlData = (Handle) mh;
-
- /* we handle our own control action, so when we get clicked
- let us know */
- (**theControl).contrlAction = (ProcPtr) -1;
-
- if(mh)
- {
- (**theControl).contrlMin = 1;
-
- /* the maximum control value is the number of items in
- the menu */
- (**theControl).contrlMax = CountMItems(mh);
-
- /* make sure the control value is within bounds */
- SetCtlValue(theControl, (**theControl).contrlValue);
-
- /* set a check mark on the appropriate item in the menu */
- CheckItem(mh, (**theControl).contrlValue, TRUE);
-
- /* need to set the rect to the maxSize of the menu! */
- CalcPopUpRect(varcode, theControl, &rect);
- (**theControl).contrlRect = rect;
- }
- }
-
- /* The control has been destroyed, destroy the menu we created also */
- void DispPopUp(varcode, theControl, param)
- int varcode;
- ControlHandle theControl;
- long param;
- {
- MenuHandle mh;
-
- if((mh = GetMenuHandle(theControl)) != NULL)
- {
- DeleteMenu((**theControl).contrlRfCon);
- DisposeMenu(mh);
- }
- }
-
- /* the control has been clicked in and needs to be dragged,
- the special case here is that if param is 0 the control
- manager wants the whole control dragged (for instance
- by ResEdit) and we don't muck with that, just let the
- control manager handle it */
- DragPopUp(varcode, theControl, param)
- int varcode;
- ControlHandle theControl;
- long param;
- {
- long menu;
- Rect rect, invert;
- MenuHandle mh;
-
- if(!param) /* if drag whole control, let control manager do it */
- {
- return 0;
- }
-
- /* get the menu handle for our menu. If we don't have a menu
- there's not much else we can do */
- if(mh = GetMenuHandle(theControl))
- {
- /* find out where our control is on the screen */
- CalcPopUpRect(varcode, theControl, &rect);
-
- /* invert the title area like the user interface
- guidelines say we should */
- SetRect(&invert, (**theControl).contrlRect.left, rect.top, rect.left, rect.bottom);
- if(*(**theControl).contrlTitle)
- InvertRect(&invert);
-
- /* convert the coordinates because popupmenuselect wants them in
- global and we've so far got them in local. Note that we align
- the top left of the currently selected item with the top left
- of where it's currently drawn so that (like the user interface
- demands) the user doesn't get to change things by surprise. */
- LocalToGlobal((Point *) &rect);
-
- menu = PopUpMenuSelect(mh, rect.top, rect.left, (**theControl).contrlValue);
-
- /* uninvert the title area */
- if(*(**theControl).contrlTitle)
- InvertRect(&invert);
-
- /* if something was chosen from the menu we have to fix all the
- values, easiest way is to erase the control and change it's
- value, that way the update manager will get it redrawn */
- if(menu & 0xffff0000)
- {
- /* restore the coordinates so that we can properly erase the old
- control */
- GlobalToLocal((Point *) &rect);
-
- rect.right += 1;
- rect.bottom += 1;
- EraseRect(&rect);
- SetCtlValue(theControl, menu & 0xffff);
- }
- }
-
- /* in any case, tell the control manager we done everything that needs
- don'in */
- return 1;
- }
-
- /* arg, I'm not sure why this is really necessary but it seems the control
- manager is a little confused. If we just call the above it all seems to
- work though */
- void TrackPopUp(varcode, theControl, param)
- int varcode;
- ControlHandle theControl;
- long param;
- {
- DragPopUp(varcode, theControl, param);
- }
-
- /* figger out what area of the screen the selectable portion of the control
- occupies. This is one menu line high, the width of the appropriate menu
- item, and possibly offset to right to center the item. */
- void CalcPopUpRect(varcode, theControl, pRect)
- int varcode;
- ControlHandle theControl;
- Rect *pRect;
- {
- Rect rect;
- char item[256];
- MenuHandle mh;
-
- rect = (**theControl).contrlRect;
-
- /* if we don't have a menu our area is the same as the control's, otherwise
- it's somewhat smaller */
- if(mh = GetMenuHandle(theControl))
- {
- /* get the text that will eventually be drawn */
- GetItem((**theControl).contrlData, (**theControl).contrlValue, item);
-
- /* if there's a title that move the left over the width of the title
- plus a little fudge */
- if(*(**theControl).contrlTitle)
- rect.left += StringWidth((**theControl).contrlTitle) + HSeparation;
-
- /* set the width of the region to the width of the item to be drawn plus fudge */
- CalcMenuSize(mh); /* do a recalc to get the proper menuWidth */
- rect.right = rect.left + (*mh)->menuWidth /*StringWidth(item) + HSpace * 2*/;
-
- /* the top is the bottom minus fudge minus the height of an item. We
- could have done this from the top I suppose... */
- rect.top = rect.bottom - VSpace - GetFontHeight();
-
- /* if the item should be centered than offset it by an appropriate amount */
- if(varcode)
- OffsetRect(&rect, ((**theControl).contrlRect.right - (**theControl).contrlRect.left
- + rect.left - rect.right) / 2, 0);
-
- /* store the result in the callers cubby hole */
- *pRect = rect;
- }
- else
- SetRect(pRect, rect.top, rect.left, rect.top, rect.left);
- }
-
- /* find the menu handle for our menu. It's either in the data field of the control
- or can be found by searching for a menu of the correct menu id in the menu bar. */
- MenuHandle GetMenuHandle(theControl)
- ControlHandle theControl;
- {
- MenuHandle mh;
-
- if(!(mh = (**theControl).contrlData))
- mh = GetMHandle((**theControl).contrlRfCon);
- return mh;
- }
-
- /* figure out how high a line is in the current font */
- GetFontHeight()
- {
- FontInfo fi;
-
- GetFontInfo(&fi);
- return fi.ascent + fi.leading + fi.descent;
- }
-
- /* figure out how far above the base line a character should be drawn */
- GetFontOffset()
- {
- FontInfo fi;
-
- GetFontInfo(&fi);
- return fi.leading + fi.descent;
- }
-
-
- /* the primary entry point to the control. The only purpose here is to switch
- out into the appropriate routine. Note that two of the routines (HitPopUp
- and DragPopUp) are defined to return values, they return immediately. All
- the others will return 0 at the bottom just for propriety's sake. */
- pascal long main(varcode, theControl, message, param)
- short varcode;
- ControlHandle theControl;
- short message;
- long param;
- {
- switch(message)
- {
- case drawCntl: DrawPopUp(varcode, theControl, param); break;
- case testCntl: return HitPopUp(varcode, theControl, param);
- case initCntl: NewPopUp(varcode, theControl, param); break;
- case calcCRgns: CalcPopUp(varcode, theControl, param); break;
- case dragCntl: return DragPopUp(varcode, theControl, param);
- case dispCntl: DispPopUp(varcode, theControl, param); break;
- case autoTrack: TrackPopUp(varcode, theControl, param); break;
- }
-
- return 0;
- }
-